package com.cdeledu.thread4.c05;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeoutException;

/**
 * 是Lock的一个Boolean实现，通过控制一个Boolean变量的开关来决定是否允许当前的线程获取该锁
 * 
 * @author HP
 *
 */
public class BooleanLock implements Lock {

	/**
	 * 当前拥有锁的线程
	 * 
	 */
	private Thread currentThread;
	/**
	 * false代表当前该锁没有被任何线程获得或者已经释放，true代表该锁已经被某个线程获得，该线程就是currentThread
	 * 
	 */
	private boolean locked = false;
	/**
	 * 存储哪些线程在获取当前线程时进入了阻塞状态
	 * 
	 */
	private final List<Thread> blockedList = new ArrayList<>();

	@Override
	public void lock() throws InterruptedException {
		// 使用同步代码块的方式进行方法同步
		synchronized (this) {
			// 如果当前锁已经被某个线程获得，则该线程将加入阻塞队列，并且使当前线程wait释放对this monitor的所有权
			while (locked) {
				// 暂存当前线程
				final Thread tempThread = Thread.currentThread();
				try {
					if (!blockedList.contains(tempThread))
						blockedList.add(tempThread);
					this.wait();
				} catch (InterruptedException e) {
					// 如果当前线程在wait时被中断，则从blockedList中将其删除，避免内存泄漏
					blockedList.remove(tempThread);
					// 继续抛出中断异常
					throw e;
				}
			}
			// 如果当前锁没有被其他线程获得，则该线程将尝试从阻塞队列中删除自己（注意：如果当前线程从未进入过阻塞队列，删除方法不会有任何影响；如果当前线程是从wait
			// set中被唤醒的，则需要从阻塞队列中将自己删除）
			blockedList.remove(Thread.currentThread());
			this.locked = true;
			// 记录获取锁的线程
			this.currentThread = Thread.currentThread();
		}
	}

	@Override
	public void lock(long mills) throws InterruptedException, TimeoutException {
		synchronized (this) {
			// 如果mills不合法，则默认调用lock（）方法，当然也可以抛出参数非法的异常，一般来说，抛出异常是一种比较好的做法
			if (mills <= 0) {
				this.lock();
			} else {
				long remainingMills = mills;
				long endMills = System.currentTimeMillis() + remainingMills;
				while (locked) {
					// 如果remainingMills小于等于0，则意味着当前线程被其他线程唤醒或者在指定的wait时间到了之后还没有获得锁，这种情况下会抛出超时的异常
					if (remainingMills <= 0)
						throw new TimeoutException("can not get the lock during " + mills);
					// 暂存当前线程
					final Thread tempThread = Thread.currentThread();
					try {
						if (!blockedList.contains(tempThread))
							blockedList.add(tempThread);
						// 等待remainingMills的毫秒数，该值最开始是由其他线程传入的，但在多次wait的过程中会重新计算
						this.wait(remainingMills);
					} catch (InterruptedException e) {
						// 如果当前线程在wait时被中断，则从blockedList中将其删除，避免内存泄漏
						blockedList.remove(tempThread);
						// 继续抛出中断异常
						throw e;
					}
					// 重新计算remainingMills时间
					remainingMills = endMills - System.currentTimeMillis();
				}
				// 获得该锁，并且从block列表中删除当前线程，将locked的状态修改为true并且指定获得锁的线程就是当前线程
				blockedList.remove(Thread.currentThread());
				this.locked = true;
				this.currentThread = Thread.currentThread();
			}
		}
	}

	@Override
	public void unlock() {
		synchronized (this) {
			// 判断当前线程是否为获取锁的那个线程，只有加了锁的线程才有资格进行解锁
			if (currentThread == Thread.currentThread()) {
				this.locked = false;
				// 通知其他在wait set中的线程，你们可以再次尝试抢锁了，这里使用notify和notifyAll都可以
				this.notifyAll();
			}
		}
	}

	@Override
	public List<Thread> getBlockedThreads() {
		return Collections.unmodifiableList(blockedList);
	}

}
